package org.zebra.mybatis.plus.config;

import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.type.TypeDescription;
import org.apache.ibatis.reflection.ReflectionException;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.dromara.streamquery.stream.core.collection.Maps;
import org.dromara.streamquery.stream.core.reflect.ReflectHelper;
import org.dromara.streamquery.stream.plugin.mybatisplus.Database;
import org.dromara.streamquery.stream.plugin.mybatisplus.engine.configuration.StreamClassPathScanner;
import org.dromara.streamquery.stream.plugin.mybatisplus.engine.constant.PluginConst;
import org.dromara.streamquery.stream.plugin.mybatisplus.engine.mapper.DynamicMapperHandler;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.zebra.mybatis.plus.core.MapperPlus;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 自定义handler 为了能够使用自己的Mapper
 *
 * @author zhanghongbin
 */
public class DynamicMapperPlusHandler extends DynamicMapperHandler {

    public DynamicMapperPlusHandler(SqlSessionFactory sqlSessionFactory, Collection<Class<?>> entityClassList) {
        // entityClassList 必须为空
        super(sqlSessionFactory, entityClassList);

        // 获取包信息
        AutowireCapableBeanFactory autowireCapableBeanFactory =
                SpringUtil.getApplicationContext().getAutowireCapableBeanFactory();
        Set<String> packageSet = new HashSet<>();
        packageSet.addAll(AutoConfigurationPackages.get(autowireCapableBeanFactory));
        // 获取实体
        StreamClassPathScanner scanner = new StreamClassPathScanner(false);
        scanner.setAnnotation(TableName.class);
        scanner.registerFilters();
        entityClassList = scanner.scan(packageSet);
        // 动态构建mapper
        Configuration configuration = sqlSessionFactory.getConfiguration();
        if (configuration instanceof MybatisConfiguration) {
            MybatisConfiguration mybatisConfiguration = (MybatisConfiguration) configuration;
            entityClassList.forEach(entityClass -> buildMapper(mybatisConfiguration, entityClass, MapperPlus.class));
        }
    }

    public static void buildMapper(
            Configuration configuration, Class<?> entityClass, Class<? extends BaseMapper> mapperClass) {
        Map<Class<?>, Class<?>> value;
        try {
            Field field = ReflectHelper.getField(Database.class, "ENTITY_MAPPER_CLASS_CACHE");
            value = (Map<Class<?>, Class<?>>) field.get(null);
        } catch (Exception e) {
            throw new ReflectionException(e);
        }
        // 如果存在自定义mapper，则不需要动态生成
        // todo 需要验证
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
        if (tableInfo != null) return;

        // 获取字段的值
        Maps.computeIfAbsent(value, entityClass, k -> {
            Class<?> dynamicMapper = new ByteBuddy()
                    .makeInterface(TypeDescription.Generic.Builder.parameterizedType(mapperClass, entityClass)
                            .build())
                    .name(String.format("%s.%sMapper", PluginConst.DYNAMIC_MAPPER_PREFIX, entityClass.getName()))
                    .make()
                    .load(ClassUtils.class.getClassLoader())
                    .getLoaded();
            configuration.addMapper(dynamicMapper);
            return dynamicMapper;
        });
    }
}
